#include <iostream>

#include "glad/glad.h"

#include <GLFW/glfw3.h>

#include "checkError.h"

#include "glframework/shader.h"

#include "glframework/texture.h"

#include "application/Application.h"

/*
目标：Mipmap实验——生成mipmap测试
1. 生成mipmap的各个级别
2. 修改vs使得三角形随着时间变小
*/

// 声明且实现一个响应窗体大小变化的函数
void onResize(int width, int height) {
  GL_CALL(glViewport(0, 0, width, height));
}
GLuint VAO = 0;
Shader *sd = nullptr;
Texture *textureGrass = nullptr;
Texture *textureEarth = nullptr;
Texture *textureNoice = nullptr;

// 声明且实现一个键盘消息回调函数
void onKeyOperation(int key, int action, int mods) {
  std::cout << key << std::endl;
}

void prepareSingleBuffer() {
  // 1. 准备顶点位置数据与颜色数据
  float positions[] = {-0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f};
  float colors[] = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
  // 2. 为位置&颜色数据各自生成一个VBO
  GLuint posVBO = 0, colorVBO = 0;
  GL_CALL(glGenBuffers(1, &posVBO));
  GL_CALL(glGenBuffers(1, &colorVBO));
  // 3. 给两个分开的VBO各自填充数据
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, posVBO));
  GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions,
                       GL_STATIC_DRAW));
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, colorVBO));
  GL_CALL(
      glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW));
  // 4. 生成VAO并绑定
  GLuint VAO = 0;
  glGenVertexArrays(1, &VAO);
  glBindVertexArray(VAO);

  // 5. 分别将位置/颜色属性信息加入VAO中
  // 5.1 描述位置属性
  glEnableVertexAttribArray(0);
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
  // 5.2 描述颜色属性
  glEnableVertexAttribArray(1);
  glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);

  glBindVertexArray(0);
}

void prepareInterleavedBuffer() {
  // 1. 准备好Interleaved数据（位置+颜色）
  float vertices[] = {-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, 0.0f,
                      0.0f,  1.0f,  0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f,  1.0f};
  GLuint VBO = 0;
  // 2. 创建唯一的VBO
  GL_CALL(glGenBuffers(1, &VBO));
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, VBO));
  GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
                       GL_STATIC_DRAW));
  // 3. 创建并绑定VAO
  glGenVertexArrays(1, &VAO);
  glBindVertexArray(VAO);

  // 4. 为VAO加入位置和颜色的描述信息
  // 4.1 位置描述信息
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, VBO));
  GL_CALL(glEnableVertexAttribArray(0));
  GL_CALL(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float),
                                (void *)0));

  // 4.2 颜色描述信息
  GL_CALL(glEnableVertexAttribArray(1));
  GL_CALL(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float),
                                (void *)(3 * sizeof(float))));

  // 5. 扫尾工作：解绑当前VAO
  glBindVertexArray(0);
}

void prepareVAOForGLTriangles() {
  // 1. 准备顶点位置信息
  float positions[] = {
      -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f,
      0.5f,  0.5f,  0.0f, 0.8f, 0.8f,  0.0f, 0.8f, 0.0f, 0.0f,
  };
  // 2. 生成VBO
  GLuint posVBO;
  glGenBuffers(1, &posVBO);
  glBindBuffer(GL_ARRAY_BUFFER, posVBO);
  glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);

  // 3. 生成VAO
  glGenVertexArrays(1, &VAO);
  glBindVertexArray(VAO);

  // 4. 描述位置属性
  glBindBuffer(GL_ARRAY_BUFFER, posVBO);
  glEnableVertexAttribArray(0);
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);

  // 5. 解绑
  glBindVertexArray(0);
}

void prepareVAO() {
  // 1. 准备positions colors
  float positions[] = {-0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f,
                       -0.5f, 0.5f,  0.0f, 0.5f, 0.5f,  0.0f};
  float colors[] = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
                    0.0f, 0.0f, 1.0f, 0.5f, 0.5f, 0.5f};
  float uvs[] = {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f};
  // float uvs[] = {0.0f, 0.0f, 4.0f, 0.0f, 0.0f, 8.0f, 4.0f, 8.0f};
  unsigned int indices[] = {0, 1, 2, 2, 1, 3};
  // 2. 创建VBO
  GLuint posVBO;
  glGenBuffers(1, &posVBO);
  glBindBuffer(GL_ARRAY_BUFFER, posVBO);
  glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);

  GLuint colorVBO;
  glGenBuffers(1, &colorVBO);
  glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
  glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);

  GLuint uvVBO;
  glGenBuffers(1, &uvVBO);
  glBindBuffer(GL_ARRAY_BUFFER, uvVBO);
  glBufferData(GL_ARRAY_BUFFER, sizeof(uvs), uvs, GL_STATIC_DRAW);
  // 3. 创建EBO
  GLuint EBO;
  glGenBuffers(1, &EBO);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,
               GL_STATIC_DRAW);
  // 4. 创建VAO
  glGenVertexArrays(1, &VAO);
  glBindVertexArray(VAO);
  // 动态获取位置
  // GLint posLoc = glGetAttribLocation(sd->mProgram, "aPos");
  // GLint colorLoc = glGetAttribLocation(sd->mProgram, "aColor");
  // 5. 绑定VBO,EBO加入属性描述信息
  // 5.1 加入位置属性描述信息
  glBindBuffer(GL_ARRAY_BUFFER, posVBO);
  glEnableVertexAttribArray(0);
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void *)0);
  // 5.2 加入颜色属性描述信息
  glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
  glEnableVertexAttribArray(1);
  glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void *)0);
  // 5.3 加入uv属性描述信息
  glBindBuffer(GL_ARRAY_BUFFER, uvVBO);
  glEnableVertexAttribArray(2);
  glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void *)0);
  // 5.2 加入EBO到当前的VAO中
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);

  glBindVertexArray(0);
}

void prepareShader() {
  sd = new Shader("assets/shaders/test.vert", "assets/shaders/test.frag");
}

void prepareTexture() {
  textureGrass = new Texture("assets/textures/temple.jpg", 0);
}
void render() {
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  // 执行画布清理操作
  GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
  // 1. 绑定当前的program
  sd->begin();
  sd->setInt("sampWall", 0);
  sd->setFloat("time", glfwGetTime());
  // 2. 绑定当前的vao
  GL_CALL(glBindVertexArray(VAO));
  // 3. 发出绘制指令
  // glDrawArrays(GL_LINE_STRIP, 0, 6);
  glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void *)0);
  glBindVertexArray(0);

  sd->end();
}

int main() {
  std::cout << "过去的积累还是有用的，但关键在于整合并形成习惯，才能发挥作用"
            << std::endl;

  Application *app = Application::getInstance();

  app->setResizeCallback(onResize);
  app->setKeyOperationCallback(onKeyOperation);

  if (!app->init(800, 600)) {
    return -1;
  }

  /* // 设置监听，监听窗体大小变化的消息
  glfwSetFramebufferSizeCallback(window, frameBufferSizeCallBack);
  // 设置监听，监听键盘消息
  glfwSetKeyCallback(window, keyCallBack); */
  // glViewport(0, 0, 800, 600);
  GL_CALL(glViewport(0, 0, 800, 600));
  // 设置openGL视口以及清理颜色
  // glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
  GL_CALL(glClearColor(0.2f, 0.3f, 0.3f, 1.0f));
  // prepareVAOForGLTriangles();
  prepareShader();
  prepareVAO();
  prepareTexture();
  // 3 执行窗体循环
  while (app->update()) {
    render();
  }

  app->destroy();
  delete textureGrass;
  delete textureEarth;
  return 0;
}
